import React from 'react';
import $ from 'jquery';
import _ from 'underscore';
import cx from 'classnames';

export default class AutoComplete extends React.Component{
  constructor(props, context) {
    super(props, context);

    this.state = {
      showResults: false,
      results: []
    };

    this.closeMenu = this.closeMenu.bind(this);
    this.openMenu = this.openMenu.bind(this);
    this.handleKeyDown = this.handleKeyDown.bind(this);
    this.handleInput = _.chain(this.handleInput).bind(this).debounce(500).value();
  } // constructor

  handleKeyDown(ev) {
    let skip = false
      , indx = -1
      , len = _.chain(this.state.results)
               .map(p => p.options ? (p.options.length + 1) : 1)
               .reduce((x, y) => x + y, 0)
               .value();

    this.$root.find(this.optionSelector).each((n, i) => {
      if ($(i).is('.selected')) {
        indx = n
      };
    });

    switch (ev.key) {
      case 'Escape':
        if (this.state.showResults) {
          this.setState({
            showResults: false
          });
        }
        break;
      case 'Enter':
        if (this.state.showResults) {
          if (indx !== -1) { // 手工触发选定项的鼠标点击事件
            let item = null;
            if (this.state.results[0].group === undefined) {
              item = _.chain(this.state.results)
                      .find((p, i) => i === indx)
                      .value();
            } else {
              item = _.chain(this.state.results)
                      .map(p => [p, ...p.options])
                      .flatten(true)
                      .find((p, i) => i === indx)
                      .value();
            }

            if (item) {
              this.handleClick(item);
            }
          }
        } else {
          this._fetch(this.$input.val(), { source: 'enter' });
        }
        break;
      case 'ArrowUp':
        if (this.state.results.length && !this.state.showResults) {
          this.setState({
            showResults: true
          });
        } else {
          indx--;

          if (indx === -1) {
            indx = len - 1;
          } else if (indx === len) {
            indx = 0;
          }

          this.scrollContainer(indx);
        }
        break;
      case 'ArrowDown':
        if (this.state.results.length && !this.state.showResults) {
          this.setState({
            showResults: true
          });
        } else {
          indx++;

          if (indx === -1) {
            indx = len - 1;
          } else if (indx === len) {
            indx = 0;
          }

          this.scrollContainer(indx);
        }
        break;
      default:
        skip = true
        break;
    }

    if (!skip) {
      ev.preventDefault();
    }
  } // handleKeyDown

  scrollContainer(index) {
    let data = this.$root.find(this.optionSelector);
    if (!data.length) {
      return;
    }

    let target = data[index]
      , { container } = this.refs;

    data.removeClass('selected').eq(index).addClass('selected');
    let hasGroup = this.state.results[0].group !== undefined;
    if (hasGroup) {
      container.scrollTop = target.offsetTop + target.clientHeight - container.offsetHeight + 3;
    }
  } // scrollContainer

  handleInput(ev) {
    if (!this.$input) {
      return;
    }

    let text = this.$input.val();
    if (this.__inputValue === text) {
      return;
    }
    this.__inputValue = text;

    if (text.length <= this.props.limitSymbolLength) {
      return;
    }

    if (this.props.enterSearch === false) {
      this._fetch(text, { source: 'normal' });
    }
  } // handleInput

  handleClick(item) {
    this.setState({
      showResults:false
    }, () => this._resolve(item));
  } // handleClick

  _fetch(text, option){
    this.props
        .promiseFn(text, option)
        .then(data => this.setState({
          showResults: true,
          results: this.props.parseFn(data)
        }));
  } // _fetch

  _resolve(item) {
    let func = () => {
      this.props.onComplete(item);
      let label = item.label || item.group;
      this.__inputValue = label;
      this.$input.val(label);
    };
    if (this.state.showResults) {
      this.setState({
        showResults: false
      }, func);
    } else {
      func();
    }
  } // _resolve

  openMenu() {
    if (this.state.results.length) {
      this.setState({
        showResults: true
      });
    }
  } // openMenu

  closeMenu({ target }) {
    if (this.state.showResults && !$.contains(this.refs.root, target)) {
      this.setState({
        showResults: false
      });
    }
  } // closeMenu

  componentDidMount(){
    $('body').on('click', this.closeMenu);

    this.$input = $(this.refs.searchText);
    this.$root = $(this.refs.root);
  } // componentDidMount

  componentWillUnmount(){
    $('body').off('click', this.closeMenu);

    this.$input = null;
    this.$root = null;
  } // componentWillUnmount

  renderGroupBody() {
    return (
      <div className="dropdown-list">
        <div className="dropdown-container" ref="container">
          {
            this.state.results.map((p, i) => (
              <div key={ p.group } className="dropdown-item">
                <div
                  className={ cx('dropdown-title', {
                    selected: i === 0
                  }) }
                  onClick={ this.handleClick.bind(this, p) }
                  onMouseUp={ this.handleClick.bind(this, p) }>
                  { p.group }
                </div>
                {
                  _.map(p.options, item => (
                    <div
                      key={ item.label }
                      className="dropdown-option"
                      onClick={ this.handleClick.bind(this, item) }
                      onMouseUp={ this.handleClick.bind(this, item) }>
                      { item.label }
                    </div>
                  ))
                }
              </div>
            ))
          }
        </div>
      </div>
    );
  } // renderGroupBody

  renderCommonBody() {
    return (
      <ul>
        {
          this.state.results.map((r, i) => (
            <li key={ r.label + i } data-role="option">
              <a
                href="javascript:;"
                className={ i === 0 ? 'selected' : '' }
                onClick={ this.handleClick.bind(this, r) }
                onMouseUp={ this.handleClick.bind(this, r) }>
                { r.label }
              </a>
            </li>
          ))
        }
      </ul>
    );
  } // renderCommonBody

  renderBody() {
    if (!this.state.results.length) {
      return (
        <ul>
          <li>查询不到结果</li>
        </ul>
      );
    }

    let hasGroup = this.state.results[0].group !== undefined;
    if (hasGroup) {
      this.optionSelector = '.dropdown-title, .dropdown-option';
    } else {
      this.optionSelector = '[data-role="option"] a';
    }

    return hasGroup ? this.renderGroupBody() : this.renderCommonBody();
  } // renderBody

  render(){
    return (
      <div className="autocomplete-box" ref="root">
        <input
          type="text"
          ref="searchText"
          placeholder={ this.props.placeholder }
          className={ this.props.className }
          onFocus={ this.openMenu }
          defaultValue={ this.props.defaultValue }
          onKeyUp={ this.handleInput }
          onKeyDown={ this.handleKeyDown } />
        {
          this.state.showResults ? this.renderBody() : null
        }
      </div>
    )
  }
}

AutoComplete.defaultProps = {
  placeholder: null,
  parseFn: a => a,
  onComplete: a => a,
  enterSearch: false,
  limitSymbolLength: 2
}
